/*
 *  class_parser.c
 *
 *  Copyright (C) 2011  Jason<linux8192@gmail.com>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include "debug.h"
#include "type.h"
#include "big_endian_tool.h"
#include "classfile.h"
#include "constant_info.h"

static int parse_interfaces(const char *buf, const int count, u2 **interface_list)
{
	u2	*ilist;
	int	i;

	if (count != 0 && (!interface_list || !buf)) {
		LOGE("NULL pointer error! %s:%d\n", __FUNCTION__, __LINE__);
		return;
	}

	ilist = *interface_list;

	for (i = 0; i < count; ++i, buf += 2) {
		ilist[i] = read_u2(buf);
	}

	return i * 2;
}

void dump_interfaces(u2 *list, int count)
{
	int	i;

	printf("%s:\n", __FUNCTION__);

	for (i = 0; i < count; ++i) {
		printf("\t%u\n", list[i]);
	}

	if (i == 0) {
		printf("\tno interfaces info\n");
	}
}

class_file_desc_t *parse_class_descriptor(const char *cls_buf, const int cls_buf_len)
{
	class_file_desc_t	*cls_desc;
	int			offset = 0;
	cp_info_t		**cp_arrlist;
	int			cp_count;
	int			tmp;
	char			*buf_cur = cls_buf;
	u2			*interfaces_list;

	if (!(cls_desc = (class_file_desc_t *)malloc(sizeof(class_file_desc_t)))) {
		LOGE("Allocate class file desc failed !\n");
		return NULL;
	}
	
	bzero(cls_desc, sizeof(class_file_desc_t));

	cls_desc->magic = read_u4_and_move_cur(&buf_cur);
	cls_desc->minor_version = read_u2_and_move_cur(&buf_cur);
	cls_desc->major_version = read_u2_and_move_cur(&buf_cur);
	cls_desc->constant_pool_count = cp_count = read_u2_and_move_cur(&buf_cur);

	cp_arrlist = malloc(sizeof(cp_info_t *) * cp_count);
	buf_cur += parse_constant_pool(buf_cur, cp_arrlist, cp_count - 1);
	cls_desc->constant_pool = cp_arrlist;

	cls_desc->access_flags = read_u2_and_move_cur(&buf_cur);

	//dump_n_bytes(buf_cur, 16);

	cls_desc->this_class = read_u2_and_move_cur(&buf_cur);
	cls_desc->super_class = read_u2_and_move_cur(&buf_cur);

	LOGV("this = %d, super = %d\n", cls_desc->this_class, cls_desc->super_class);

	cls_desc->interfaces_count = read_u2_and_move_cur(&buf_cur);

	LOGV("interfaces offset ......0x%08x(%d)\n", buf_cur - cls_buf, buf_cur - cls_buf);

	interfaces_list = malloc(cls_desc->interfaces_count * sizeof(u2));
	buf_cur += parse_interfaces(buf_cur, cls_desc->interfaces_count, &interfaces_list);
	cls_desc->interfaces = interfaces_list;

	LOGV("fields offset ......0x%08x(%d)\n", buf_cur - cls_buf, buf_cur - cls_buf);

	//dump_n_bytes(buf_cur, 32);

	cls_desc->fields_count = read_u2_and_move_cur(&buf_cur);
	cls_desc->fields_info = parse_fields_info(buf_cur, cls_desc->fields_count,
						  cls_desc->constant_pool, &tmp);
	buf_cur += tmp;

	// TODO
	// methods
	// attributes

	return cls_desc;
}

void dump_class(class_file_desc_t *cls_desc)
{
	char *tmp_buf = malloc(512);
	if (!tmp_buf) {
		LOGE("Allocate memory failed, %s:%d\n", __FUNCTION__, __LINE__);
		return;
	}

	if (!cls_desc) {
		LOGE("clas descriptor is NULL\n");
		return;
	}

	printf("MAGIC\t\t = 0x%08x\n", cls_desc->magic);

	printf("MINOR_VER\t = 0x%04x\n", cls_desc->minor_version);

	printf("MAJOR_VER\t = 0x%04x\n", cls_desc->major_version);

	printf("CP_COUNT\t = 0x%04x\n", cls_desc->constant_pool_count);

	dump_constant_pool(cls_desc->constant_pool, cls_desc->constant_pool_count);

	printf("ACCESS_FLAG\t = 0x%04x\n", cls_desc->access_flags);

	copy_class_name_from_cp(&tmp_buf, cls_desc->constant_pool, cls_desc->this_class);
	printf("THIS CLASS\t index = %d, name = %s\n", cls_desc->this_class, tmp_buf);

	copy_class_name_from_cp(&tmp_buf, cls_desc->constant_pool, cls_desc->super_class);
	printf("SUPER CLASS\t index = %d, name = %s\n", cls_desc->super_class, tmp_buf);

	dump_interfaces(cls_desc->interfaces, cls_desc->interfaces_count);

	dump_fields_info(cls_desc->fields_info, cls_desc->fields_count);
}
